//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    #region NameSpace

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using System.ComponentModel;
    using System.Linq;

    #endregion

    /// <summary>
    /// Interaction logic for WorkFlowTree.xaml
    /// </summary>
    public partial class WorkflowTree : UserControl
    {
        #region events

        /// <summary>
        /// Event declaration to handle MouseDown Event
        /// </summary>
        public event MouseEventHandler OnWorkflowMouseDown;

        /// <summary>
        /// Event declaration to handle Key down Event
        /// </summary>
        public event KeyboardEventHandler OnWorkflowEnterDown;

        /// <summary>
        /// Event declaration to handle Key down Event
        /// </summary>
        public event EventHandler OnWorkflowDelete;

        /// <summary>
        /// Event for the label operation.
        /// </summary>
        public event EventHandler OnWorkflowLabel;

        /// <summary>
        /// Fired when user clicks on Create workflow category in the context menu of a category node.
        /// </summary>
        public event EventHandler CreateWorkflowCategory;

        /// <summary>
        /// Holds the last selected node
        /// </summary>
        private TreeViewItem selectedWorkflowNode;

        #endregion

        #region Constructor

        /// <summary>
        ///  Constructor : Builds the WorkFlow Tree based on  WorkflowContainer
        /// </summary>
        /// <param name="workFlowContainer">WorkflowContainer</param>
        public WorkflowTree(Collection<WorkflowCategoryModel> workflowCategoryList)
        {
            if (workflowCategoryList == null)
            {
                throw new ArgumentNullException("workflowCategoryList");
            }

            InitializeComponent();

            // Build the workflow tree.
            this.BuildWorkflowTree(workflowCategoryList);
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Set the name of the workflow in the tree. Used when the label of the workflow changes.
        /// </summary>
        /// <param name="treeviewItem">The treeview item whose header needs change.</param>
        /// <param name="workflow">The metadata of the workflow.</param>
        public static void ChangeDisplayName(HeaderedItemsControl treeViewItem, WorkflowMetadata workflow)
        {
            treeViewItem.Header = workflow.DisplayName;
        }

        /// <summary>
        /// Refreshes the WorkFlow Tree
        /// </summary>
        /// <param name="paramActivityList">List<Activity> </param>
        public void RefreshWorkflowTree(Collection<WorkflowCategoryModel> workflowCategoryList)
        {
            if (workflowCategoryList == null)
            {
                throw new ArgumentNullException("workflowCategoryList");
            }

            this.ClearTreeNodes();
            this.BuildWorkflowTree(workflowCategoryList);
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Generates the Stack Panel with name and Description
        /// </summary>
        /// <param name="name">string</param>
        /// <param name="description">string</param>
        /// <returns></returns>
        private static TextBlock GenerateToolTip(string paramName, string paramDescription)
        {
            TextBlock toolTipTxtBlk = new TextBlock();
            toolTipTxtBlk.TextWrapping = TextWrapping.Wrap;
            toolTipTxtBlk.MaxWidth = 500;
            Run nameLabelBlockRun = new Run(UIDesignerCodeConstants.ACTIVITYTOOLTIPNAME);
            nameLabelBlockRun.FontWeight = FontWeights.Bold;
            Run nameTextBlockRun = new Run(paramName);
            Run newLineRun = new Run(Environment.NewLine);
            Run descriptionLabelBlockRun = new Run(UIDesignerCodeConstants.ACTIVITYTOOLTIPDESCRIPTION);
            descriptionLabelBlockRun.FontWeight = FontWeights.Bold;
            Run descriptionTextBlockRun = new Run(paramDescription);

            toolTipTxtBlk.Inlines.Add(nameLabelBlockRun);
            toolTipTxtBlk.Inlines.Add(nameTextBlockRun);
            toolTipTxtBlk.Inlines.Add(newLineRun);
            toolTipTxtBlk.Inlines.Add(descriptionLabelBlockRun);
            toolTipTxtBlk.Inlines.Add(descriptionTextBlockRun);

            return toolTipTxtBlk;
        }

        /// <summary>
        /// Sorts the activity list based on Activity name and the modified time.
        /// Implementation of Icomparer object
        /// </summary>
        /// <param name="activityA">Activity</param>
        /// <param name="activityB">Activity</param>
        /// <returns></returns>
        private static int WorkFlowComparer(WorkflowMetadata first, WorkflowMetadata second)
        {
            int result = 0;
            string firstStringToCompare = first.Name;
            string secondStringToCompare = second.Name;
            int compareValue = string.Compare(firstStringToCompare, secondStringToCompare, StringComparison.OrdinalIgnoreCase);
            if (compareValue != 0)
            {
                result = compareValue > 0 ? 1 : -1;
            }
            else
            {
                compareValue = DateTime.Compare(first.LastModifiedTime, second.LastModifiedTime);
                result = compareValue > 0 ? -1 : 1;
            }
            return result;
        }

        /// <summary>
        /// Creates the Image object
        /// </summary>
        /// <param name="imagepath">string path of the image</param>
        /// <returns>Image</returns>
        protected static Image GetIcon(string imagePath)
        {
            Image headerImage = new Image();
            ImageSourceConverter converter = new ImageSourceConverter();
            headerImage.Source = (ImageSource)converter.ConvertFromString(imagePath);
            headerImage.Width = 12;
            headerImage.Height = 12;
            return headerImage;
        }

        /// <summary>
        /// Event handler for the start drag event on the workflow.
        /// </summary>
        /// <param name="source">source of the drag</param>
        /// <param name="data">Drag data.</param>
        private static void StartDrag(System.Windows.DependencyObject source, object data)
        {
            try
            {
                DragDrop.DoDragDrop(source, data, DragDropEffects.Copy);
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
            catch (Exception ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Builds the WorkFlow tree by Iterating the workFlowContainer
        /// </summary>
        private void BuildWorkflowTree(Collection<WorkflowCategoryModel> categoryList)
        {
            foreach (WorkflowCategoryModel workflowCategory in categoryList)
            {
                // Build a treeview item and add it to the treeview.
                TreeViewItem categoryNode = BuildCategoryNode(workflowCategory);
                categoryNode.Margin = new Thickness(0, 3, 0, 0);
                this.WorkFlowTreeView.Items.Add(categoryNode);
            }
        }

        /// <summary>
        /// Build the category node treeview item for the category.
        /// </summary>
        /// <param name="workflowCategory">The workflow category model.</param>
        /// <returns>The category treeview item.</returns>
        private TreeViewItem BuildCategoryNode(WorkflowCategoryModel workflowCategory)
        {
            // Build a treeview item for the category.
            TreeViewItem categoryNode = new TreeViewItem();
            categoryNode.Style = this.TryFindResource(UIDesignerCodeConstants.PARENTWORKFLOWTREEITEMSTYLE) as Style;
            categoryNode.Header = workflowCategory.Label;
            categoryNode.DataContext = workflowCategory;
            categoryNode.Margin = new Thickness(0, 3, 0, 0);
            categoryNode.Items.SortDescriptions.Add(new SortDescription("Header", ListSortDirection.Ascending));

            // Add context menu.
            MenuItem createMenu = new MenuItem();
            createMenu.Header = TridentResourceManager.GetString("CreateWorkflowCategory");
            createMenu.Icon = GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/new.png");
            createMenu.Click += new RoutedEventHandler(this.OnCreateWorkflowCategory);
            ContextMenu contextMenu = new ContextMenu();
            if (workflowCategory.IsVirtualCategory)
            {
                createMenu.IsEnabled = false;
            }

            contextMenu.Items.Add(createMenu);
            categoryNode.ContextMenu = contextMenu;
            categoryNode.MouseRightButtonUp += new MouseButtonEventHandler(this.OnCategoryNodeRightClick);

            // Add the subcategories as children of this treeview item.
            IOrderedEnumerable<WorkflowCategoryModel> sortedCategoryList = workflowCategory.Categories.OrderBy(item => item.Label);
            foreach (WorkflowCategoryModel childCategory in sortedCategoryList)
            {
                TreeViewItem childItem = this.BuildCategoryNode(childCategory);
                categoryNode.Items.Add(childItem);
            }

            List<WorkflowMetadata> sortedWorkflowList = new List<WorkflowMetadata>(workflowCategory.Workflows);
            sortedWorkflowList.Sort(WorkFlowComparer);

            //Add the activities of this category
            foreach (WorkflowMetadata workflow in sortedWorkflowList)
            {
                // Populate non deleted workflows
                if (workflow.IsDeleted == false)
                {
                    TreeViewItem childActivity = this.BuildWorkflowNode(workflow);
                    categoryNode.Items.Add(childActivity);
                }
            }

            categoryNode.IsExpanded = true;
            return categoryNode;
        }

        /// <summary>
        /// Build the workflow node treeview item for the workflow.
        /// </summary>
        /// <param name="workflow">The workflow activity metadata.</param>
        /// <returns>The workflow treeview item.</returns>
        private TreeViewItem BuildWorkflowNode(WorkflowMetadata workflow)
        {
            // Build a treeview item for the activities.
            TreeViewItem workflowNode = new TreeViewItem();
            workflowNode.Style = this.TryFindResource(UIDesignerCodeConstants.CHILDWORKFLOWTREEITEMSTYLE) as Style; ;
            workflowNode.Header = workflow.DisplayName;
            workflowNode.DataContext = workflow;
            workflowNode.ToolTip = WorkflowTree.GenerateToolTip(workflow.Name, workflow.Description);
            workflowNode.Margin = new Thickness(0, 3, 0, 0);

            if (TridentAuthentication.CanUserDeleteWorkflow(workflow.OwnerId))
            {
                workflowNode.CommandBindings.Add(this.CreateCommandBindingForDelete());
            }

            // Attach the events.
            workflowNode.MouseRightButtonUp += new MouseButtonEventHandler(this.OnWorkflowNodeRightClick);
            workflowNode.PreviewMouseDoubleClick += new MouseButtonEventHandler(this.WorkFlowPreviewMouseDoubleClick);
            workflowNode.PreviewKeyDown += new KeyEventHandler(this.WorkFlowPreviewKeyDown);
            workflowNode.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(this.OnPreviewMouseLeftButtonDown);
            return workflowNode;
        }

        /// <summary>
        /// Removes all child from the tree
        /// </summary>
        private void ClearTreeNodes()
        {
            this.WorkFlowTreeView.Items.Clear();
        }

        /// <summary>
        /// Creates command bindings for delete command
        /// </summary>
        /// <returns></returns>
        private CommandBinding CreateCommandBindingForDelete()
        {
            CommandBinding deleteBinding = new CommandBinding(ApplicationCommands.Delete);
            deleteBinding.CanExecute += new CanExecuteRoutedEventHandler(DeleteBindingCanExecute);
            return deleteBinding;
        }

        /// <summary>
        /// Creates Context menu for WorkFlow tree
        /// </summary>
        /// <param name="metadata">The workflow metadata.</param>
        /// <returns>The context menu with items populated.</returns>
        private ContextMenu CreateWorkFlowContextMenu(WorkflowMetadata metadata)
        {
            ContextMenu contextMenu = new ContextMenu();
            if (TridentAuthentication.CanUserDeleteWorkflow(metadata.OwnerId))
            {
                MenuItem deleteMenu = new MenuItem();
                deleteMenu.Icon = GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/DeleteWorkflow.png");
                deleteMenu.Header = TridentResourceManager.GetString("WorkFlowDeleteContextMenu");
                deleteMenu.Click += new RoutedEventHandler(OnDeleteMenuClick);
                contextMenu.Items.Add(deleteMenu);
            }

            MenuItem labelMenu = new MenuItem();
            labelMenu.Icon = GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ScientificWorkflow/wflabel.png");

            if (string.IsNullOrEmpty(metadata.VersionLabel))
            {
                labelMenu.Header = TridentResourceManager.GetString("WorkflowLabelContextMenu");
            }
            else
            {
                // If already labelled then add the edit label header.
                labelMenu.Header = TridentResourceManager.GetString("WorkflowEditLabelContextMenu");
            }

            labelMenu.Click += new RoutedEventHandler(OnWorkflowLabelClick);
            contextMenu.Items.Add(labelMenu);

            // Set the context menu to be part of the Trident application.
            contextMenu.PlacementTarget = Application.Current.MainWindow;

            return contextMenu;

        }

        /// <summary>
        /// Handles right click on category node.
        /// Pops up the context menu.
        /// </summary>
        /// <param name="sender">
        /// Category node.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCategoryNodeRightClick(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem categoryNode = sender as TreeViewItem;
            if (categoryNode != null && categoryNode.ContextMenu != null)
            {
                categoryNode.ContextMenu.IsOpen = true;
                categoryNode.ContextMenu.Tag = categoryNode;
            }

            e.Handled = true;
        }

        /// <summary>
        /// Fired when user clicks on Create category in the context menu.
        /// </summary>
        /// <param name="sender">
        /// Create category menu item.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCreateWorkflowCategory(object sender, RoutedEventArgs e)
        {
            if (this.CreateWorkflowCategory != null)
            {
                MenuItem menuItem = sender as MenuItem;
                if (menuItem != null)
                {
                    ContextMenu contextMenu = menuItem.Parent as ContextMenu;
                    if (contextMenu != null)
                    {
                        // Fire the CreateWorkflowCategory event.
                        TreeViewItem treeViewItem = contextMenu.Tag as TreeViewItem;
                        this.CreateWorkflowCategory.Invoke(treeViewItem, new EventArgs());
                    }
                }
            }
        }

        /// <summary>
        /// Handles Right click event on the workflowNode.
        /// Shows the context menu.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">ContextMenuEventArgs.</param>
        private void OnWorkflowNodeRightClick(object sender, MouseButtonEventArgs e)
        {
            this.selectedWorkflowNode = sender as TreeViewItem;
            if (this.selectedWorkflowNode != null)
            {
                this.selectedWorkflowNode.ContextMenu = null;
                this.selectedWorkflowNode.ContextMenu = this.CreateWorkFlowContextMenu(this.selectedWorkflowNode.DataContext as WorkflowMetadata);
                this.selectedWorkflowNode.ContextMenu.IsOpen = true;


                this.selectedWorkflowNode.IsSelected = true;
            }

            e.Handled = true;
        }

        /// <summary>
        /// Event Handler for Delete Context Menu Click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDeleteMenuClick(object sender, RoutedEventArgs e)
        {
            if (null != this.OnWorkflowDelete)
            {
                this.OnWorkflowDelete.Invoke(this.selectedWorkflowNode, e);
            }
        }

        /// <summary>
        /// Event handler for the Workflow Label context menu click.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWorkflowLabelClick(object sender, RoutedEventArgs e)
        {
            if (null != this.OnWorkflowLabel)
            {
                this.OnWorkflowLabel.Invoke(this.selectedWorkflowNode, e);
            }
        }

        /// <summary>
        /// Event Handler for Delete Command Binding 
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="e">CanExecuteRoutedEventArgs</param>
        private void DeleteBindingCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (null != this.OnWorkflowDelete)
            {
                this.OnWorkflowDelete.Invoke(sender, e);
            }
        }

        /// <summary>
        /// Fired on preview mouse left button down.
        /// </summary>
        /// <param name="sender">
        /// This.
        /// </param>
        /// <param name="e">
        /// MouseButtonEventArgs.
        /// </param>
        private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ButtonState == MouseButtonState.Pressed)
            {
                WorkflowTree.StartDrag((System.Windows.DependencyObject)e.Source, ((HeaderedItemsControl)sender).DataContext);
            }
        }

        /// <summary>
        /// Event handler for Preview Mouse DoubleClick
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">MouseButtonEventArgs</param>
        private void WorkFlowPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (this.OnWorkflowMouseDown != null)
            {
                this.OnWorkflowMouseDown.Invoke(sender, e);

            }
        }

        /// <summary>
        /// Event handler for Preview Keyboard Down
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">KeyEventArgs</param>
        private void WorkFlowPreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (this.OnWorkflowEnterDown != null)
                {
                    this.OnWorkflowEnterDown.Invoke(sender, e);

                }
            }
        }

        #endregion
    }
}